package com.zhima.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zhima.constant.UserRedisConstant;
import com.zhima.dto.ChangePasswordForm;
import com.zhima.dto.CreateOjUserDTO;
import com.zhima.dto.UserDTO;
import com.zhima.dto.UserDTOExtension;
import com.zhima.entity.OjResource;
import com.zhima.entity.OjRole;
import com.zhima.entity.OjUser;
import com.zhima.entity.OjUserRole;
import com.zhima.exception.ZhimaOjException;
import com.zhima.feignclient.CheckCodeFeignClient;
import com.zhima.mapper.OjUserMapper;
import com.zhima.service.OjRoleResourceService;
import com.zhima.service.OjUserRoleService;
import com.zhima.service.OjUserService;
import com.zhima.vo.UserCenterBasicInfoVO;
import com.zhima.vo.UserRankVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import static com.zhima.exception.ErrorCode.*;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author zhima
 * @since 2023-08-23
 */
@Service
public class OjUserServiceImpl extends ServiceImpl<OjUserMapper, OjUser> implements OjUserService {
    private final BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
    @Autowired
    private OjUserRoleService ojUserRoleService;
    @Autowired
    private OjRoleResourceService ojRoleResourceService;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private CheckCodeFeignClient checkCodeFeignClient;

    @Override
    public UserDTOExtension queryUserDetailWithResources(Long userId) {
        return buildUserDTOExtension(getById(userId));
    }

    private UserDTOExtension buildUserDTOExtension(OjUser ojUser) {
        if (ojUser == null) {
            return null;
        }
        List<OjRole> roles = ojUserRoleService.queryUserRole(ojUser.getId());
        UserDTOExtension userDTOExtension = BeanUtil.copyProperties(ojUser, UserDTOExtension.class);
        userDTOExtension.setRoles(roles.stream().map(OjRole::getName).collect(Collectors.toList()));
        userDTOExtension.setResources(
                ojRoleResourceService.queryRolesResource(
                                // roleIds
                                roles.stream().map(OjRole::getId).collect(Collectors.toList()))
                        // 只需要资源编号
                        .stream().map(OjResource::getResourceCode).collect(Collectors.toList()));
        return userDTOExtension;
    }

    @Override
    public UserDTOExtension queryUserDetailWithResourcesByUserName(String userName) {
        return buildUserDTOExtension(lambdaQuery().eq(OjUser::getUserName, userName).one());
    }

    @Override
    public UserDTOExtension queryUserDetailWithResourcesByUserPhone(String phone) {
        return buildUserDTOExtension(lambdaQuery().eq(OjUser::getPhone, phone).one());
    }

    @Override
    public UserDTOExtension queryUserDetailWithResourcesByUserEmail(String email) {
        return buildUserDTOExtension(lambdaQuery().eq(OjUser::getEmail, email).one());
    }

    @Override
    public UserDTOExtension createPlainUser(CreateOjUserDTO cojUser) {
        OjUser ojUser = BeanUtil.copyProperties(cojUser, OjUser.class);
        if (!save(ojUser)) {
            throw new ZhimaOjException(USER_CREATE_FAILURE);
        }
        OjUserRole ojUserRole = new OjUserRole();
        ojUserRole.setUserId(ojUser.getId());
        // TODO 后续看看怎么把这里优化
        //  这里先写死
        ojUserRole.setRoleId(3);
        // 这里必须成功，后续加消息队列控制分布式事务
        if (!ojUserRoleService.save(ojUserRole)) {
            throw new ZhimaOjException(DISPATCH_ROLE_FAILURE);
        }
        return buildUserDTOExtension(ojUser);
    }

    @Override
    public UserDTO queryUserBasicInfoById(Long userId) {
        return BeanUtil.copyProperties(lambdaQuery().eq(OjUser::getId, userId).one(), UserDTO.class);
    }

    @Override
    public UserCenterBasicInfoVO getUserCenterInfo(Long userId) {
        OjUser one = lambdaQuery().eq(OjUser::getId, userId).one();
        UserCenterBasicInfoVO userCenterBasicInfoVO = BeanUtil.copyProperties(one, UserCenterBasicInfoVO.class);
        // 提交次数
        String submitNumStr = stringRedisTemplate.opsForValue().get(UserRedisConstant.USER_SUBMIT + userId);
        userCenterBasicInfoVO.setSubmitNum(StrUtil.isBlank(submitNumStr) ? 0 : Long.parseLong(submitNumStr));
        // AC次数
        Double acNumStr = stringRedisTemplate.opsForZSet().score(UserRedisConstant.USER_AC_NUM_BOARD, userId.toString());
        userCenterBasicInfoVO.setAcNum(acNumStr == null ? 0 : acNumStr.longValue());
        // 排名
        Long rank = stringRedisTemplate.opsForZSet().reverseRank(UserRedisConstant.USER_AC_NUM_BOARD, userId.toString());
        userCenterBasicInfoVO.setRank(rank == null ? 0 : rank + 1);
        return userCenterBasicInfoVO;
    }

    @Override
    public boolean changeUserName(Long userId, String userName) {
        return lambdaUpdate().eq(OjUser::getId, userId).set(OjUser::getUserName, userName).update();
    }

    @Override
    public boolean changePhone(Long userId, String phone, String checkCode) {
        String resp = checkCodeFeignClient.verifyPhoneCheckCode(phone, checkCode);
        if (StrUtil.isBlank(resp)) {
            return lambdaUpdate().eq(OjUser::getId, userId).set(OjUser::getPhone, phone).update();
        }
        throw new ZhimaOjException(resp);
    }

    @Override
    public boolean changeEmail(Long userId, String email, String checkCode) {
        String resp = checkCodeFeignClient.verifyEmailCheckCode(email, checkCode);
        if (StrUtil.isBlank(resp)) {
            return lambdaUpdate().eq(OjUser::getId, userId).set(OjUser::getEmail, email).update();
        }
        throw new ZhimaOjException(resp);
    }

    @Override
    public boolean changePassword(Long userId, ChangePasswordForm changePasswordForm) {
        String rightPassword = lambdaQuery().select(OjUser::getPassword).eq(OjUser::getId, userId).one().getPassword();
        if (!passwordEncoder.matches(changePasswordForm.getOldPassword(), rightPassword)) {
            throw new ZhimaOjException(OLD_PASSWORD_WRONG);
        }
        // 更新新密码
        return lambdaUpdate().eq(OjUser::getId, userId).set(OjUser::getPassword, passwordEncoder.encode(changePasswordForm.getNewPassword())).update();
    }

    @Override
    public UserRankVO[] getAcBoard(Long userId, Long pageNum, Long pageSize) {
        long start = (pageNum - 1) * pageSize;
        Set<ZSetOperations.TypedTuple<String>> pageBoard = stringRedisTemplate.opsForZSet().rangeWithScores(UserRedisConstant.USER_AC_NUM_BOARD, start, start + pageSize);
        if (pageBoard.isEmpty()) {
            return new UserRankVO[]{};
        }
        UserRankVO[] userRankVOS = new UserRankVO[pageBoard.size()];
        int index = 0;
        for (ZSetOperations.TypedTuple<String> userInfo : pageBoard) {
            UserRankVO userRankVO = new UserRankVO();
            userRankVO.setRank((int) (start + index));
            userRankVO.setAcNum(userInfo.getScore().intValue());
            userRankVO.setId(Long.parseLong(userInfo.getValue()));
            userRankVO.setUserName(lambdaQuery().eq(OjUser::getId, userRankVO.getId()).select(OjUser::getUserName).one().getUserName());
            userRankVOS[index] = userRankVO;
        }
        return userRankVOS;
    }
}
